﻿using Dyao.Book.Contract.Serialization;
using Foundation.Core;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

namespace Dyao.Book.Web.Extensions
{
    public class ApiResult
    {
        [ShowBoolDescription(FormatEnabled = false)]
        public bool IsSuccess { get; set; }

        [ShowEnumDescription(FormatEnabled = false)]
        public ApiResultStatusCode StatusCode { get; set; }


        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
        public string ErrorCode { get; set; }

        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
        public string Message { get; set; }

        [JsonProperty("errors", NullValueHandling = NullValueHandling.Ignore)]
        public Dictionary<string, string> ValidateErrors { get; set; }

        [JsonExtensionData(WriteData = true, ReadData = false)]
        public Dictionary<string, object> AddtionData { get; set; }

        public ApiResult(bool isSuccess, ApiResultStatusCode statusCode, string message = null)
        {
            IsSuccess = isSuccess;
            StatusCode = statusCode;
            Message = message ?? statusCode.ToDisplay();
        }

        #region Implicit Operators
        public static implicit operator ApiResult(OkResult _)
        {
            return new ApiResult(true, ApiResultStatusCode.Success);
        }

        public static implicit operator ApiResult(BadRequestResult _)
        {
            return new ApiResult(false, ApiResultStatusCode.BadRequest);
        }

        public static implicit operator ApiResult(BadRequestObjectResult result)
        {
            var message = result.Value?.ToString();
            if (result.Value is SerializableError errors)
            {
                var errorMessages = errors.SelectMany(p => (string[])p.Value).Distinct();
                message = string.Join(" | ", errorMessages);
            }
            return new ApiResult(false, ApiResultStatusCode.BadRequest, message);
        }

        public static implicit operator ApiResult(ContentResult result)
        {
            return new ApiResult(true, ApiResultStatusCode.Success, result.Content);
        }

        public static implicit operator ApiResult(NotFoundResult _)
        {
            return new ApiResult(false, ApiResultStatusCode.NotFound);
        }

        public static implicit operator ApiResult(ProblemDetails problem)
        {
            string message = problem.Detail ?? problem.Title;
            var result = new ApiResult(false, ApiResultStatusCode.BadRequest, message);

            if (problem is ValidationProblemDetails validation && validation.Errors.Count > 0)
            {
                result.Message = validation.Errors.FirstOrDefault(a => a.Value.Length > 0).Value[0];

                result.ValidateErrors = validation.Errors.ToDictionary(a => a.Key, a => string.Join("|", a.Value));

                result.ErrorCode = "validate.error";
            }

            if (problem.Extensions != null && problem.Extensions.Count > 0)
            {
                result.AddtionData = new Dictionary<string, object>(problem.Extensions);
            }

            return result;
        }

        public static implicit operator ApiResult(SerializableError errors)
        {
            var dict = errors.Where(a => a.Value != null).ToDictionary(a => a.Key, a => string.Join("|", (a.Value as string[])));
            var message = dict.FirstOrDefault(a => !string.IsNullOrWhiteSpace(a.Value)).Value;

            return new ApiResult(false, ApiResultStatusCode.BadRequest, message)
            {
                ErrorCode = "validate.error",
                ValidateErrors = dict
            };
        }

        #endregion
    }

    public class ApiResult<TData> : ApiResult

    {
        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
        public TData Data { get; set; }

        public ApiResult(bool isSuccess, ApiResultStatusCode statusCode, TData data, string message = null)
            : base(isSuccess, statusCode, message)
        {
            Data = data;
        }

        #region Implicit Operators
        public static implicit operator ApiResult<TData>(TData data)
        {
            return new ApiResult<TData>(true, ApiResultStatusCode.Success, data);
        }

        public static implicit operator ApiResult<TData>(OkResult _)
        {
            return new ApiResult<TData>(true, ApiResultStatusCode.Success, default);
        }

        public static implicit operator ApiResult<TData>(OkObjectResult result)
        {
            return new ApiResult<TData>(true, ApiResultStatusCode.Success, (TData)result.Value);
        }

        public static implicit operator ApiResult<TData>(BadRequestResult _)
        {
            return new ApiResult<TData>(false, ApiResultStatusCode.BadRequest, default);
        }

        public static implicit operator ApiResult<TData>(BadRequestObjectResult result)
        {
            var message = result.Value?.ToString();
            if (result.Value is SerializableError errors)
            {
                var errorMessages = errors.SelectMany(p => (string[])p.Value).Distinct();
                message = string.Join(" | ", errorMessages);
            }
            return new ApiResult<TData>(false, ApiResultStatusCode.BadRequest, default, message);
        }

        public static implicit operator ApiResult<TData>(ContentResult result)
        {
            return new ApiResult<TData>(true, ApiResultStatusCode.Success, default, result.Content);
        }

        public static implicit operator ApiResult<TData>(NotFoundResult _)
        {
            return new ApiResult<TData>(false, ApiResultStatusCode.NotFound, default);
        }

        public static implicit operator ApiResult<TData>(NotFoundObjectResult result)
        {
            return new ApiResult<TData>(false, ApiResultStatusCode.NotFound, (TData)result.Value);
        }
        #endregion
    }
}
